package com.lewis.server;

import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.HttpURLConnection;
import java.net.Socket;
import java.net.SocketException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.zip.GZIPOutputStream;

public class SocketHandler extends Thread {

	// socket timeout milliseconds
	static public int TIMEOUT = 0;
	// raw request buffer size, should be way enough to handle
	static public int BUFSIZ = 9000;

	protected Socket csocket;

	public SocketHandler(Socket socket) {
		csocket = socket;
	}

	@Override
	public void run() {
		InputStream cis = null;
		OutputStream cos = null;
		InputStream in = null;
		try {
			csocket.setSoTimeout(0);
			cis = csocket.getInputStream();
			cos = csocket.getOutputStream();
			OutputStream raw = new BufferedOutputStream(cos);
			Writer out = new OutputStreamWriter(raw); 
			// parse the request and turn it into pojo
			RequestPojo req = parseRawReq(cis);
			if (req.getMethod().equalsIgnoreCase("GET")) {
				URL url = new URL(req.getUrl());
				URLConnection conn = url.openConnection();
				HttpURLConnection httpConnection = (HttpURLConnection) conn;
				httpConnection.setRequestMethod(req.getMethod());
				for(Map.Entry<String, String> entry:req.getHeaders().entrySet()){
//					System.out.println("entry key: " + entry.getKey());
//					System.out.println("entry value: " + entry.getValue());
					httpConnection.setRequestProperty(entry.getKey(), entry.getValue());
				}
				httpConnection.connect();
				//headers
				Map<String, List<String>> headers = httpConnection.getHeaderFields();
				
				
				//dealing with byte code stuff
				String contentType = null;
				String charset = null;
				String[] contentArray = httpConnection.getContentType().split(";");
				contentType = contentArray[0].trim();
				if (contentArray.length > 1){
					charset = contentArray[1].substring(contentArray[1].indexOf("=")+1).trim();
				}
				in = httpConnection.getInputStream();
//				out.write("HTTP/1.1 200 OK\r\n");  
//                Date now = new Date();  
//                out.write("Date:"+now+"\r\n");  
//                out.write("Server: JHTTP/1.0\r\n");  
//                out.write("Content-length:"+"chunk"+"\r\n");  
//                out.write("Content-type:"+contentType+"\r\n\r\n");  
//                out.flush();
                
//				String sheader = "";
				for(Map.Entry<String, List<String>> entry:headers.entrySet()){
					String name = entry.getKey();
					if(name == null){
						out.write(entry.getValue().get(0) + "\r\n");
					} else{
						out.write(entry.getKey() + ":"+entry.getValue().get(0) + "\r\n");
					}
				}
				out.write("\r\n");
				out.flush();
//                String test = "<html><body><h1>hello bitch</h1></body></html>";
//				try {
//					cos.write(compress(test));
//				} catch (Exception e) {
//					e.printStackTrace();
//				}
//				cos.flush();
				byte bytes[] = new byte[409600];
				if (contentType.equalsIgnoreCase("text/html") && charset != null){
					int length;
					while ((length = in.read(bytes)) != -1) {
						if (length > 0) {
							String s = new String(bytes, 0, length, charset);
							//the length is different after String gets converted
							cos.write(s.getBytes(charset), 0, s.getBytes(charset).length);
							cos.flush();
						}
					}
				} else{
					int length;
					while ((length = in.read(bytes)) != -1) {
						if (length > 0) {
							cos.write(bytes, 0, length);
							cos.flush();
						}
					}
				}
			}
				
		} catch (SocketException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
//				cos.close();
				csocket.close();
				
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	private RequestPojo parseRawReq(InputStream cis) throws IOException {
		RequestPojo rpj = new RequestPojo();
		byte bytes[] = new byte[BUFSIZ];
		cis.read(bytes);
		boolean isFirstLine = true;
		boolean isLineEnds = false;
		String string = "";
		for (int i = 0; i < bytes.length; i++) {
			if (bytes[i] == '\r' || bytes[i] == '\n') {
				if (isLineEnds) {
					continue;
				}
				isLineEnds = true;
			} else {
				isLineEnds = false;
			}
			string += (char) bytes[i];
			if (isLineEnds) {
				if (isFirstLine) {
					String[] s = string.split(" ");
					rpj.setMethod(s[0].trim());
					rpj.setUrl(s[1].trim());
					isFirstLine = false;
//					System.out.println(string);
					string = "";
				} else {
					String[] s = string.split(":",2);
					rpj.getHeaders().put(s[0].trim(), s[1].trim());
//					System.out.println(string);
					string = "";
				}
			}

		}
		return rpj;
	}
	
	public static byte[] compress(String str) throws Exception {
        ByteArrayOutputStream obj=new ByteArrayOutputStream();
        GZIPOutputStream gzip = new GZIPOutputStream(obj);
        gzip.write(str.getBytes("UTF-8"));
        gzip.close();
        return obj.toByteArray();
     }

}
